﻿using System.Collections;
using System.Collections.Generic;

namespace SGLisp
{

    public interface IFunction 
    {
        ASTNode Exec(List<ASTNode> args, Context context);
    }


    public static class CoreFunction
    {

        public static void SetCoreFunction(Context context)
        {
            context.Define("+", new FunctionNode(Add));
            context.Define("-",new FunctionNode(_sub));
            context.Define("*", new FunctionNode(_mul));
            context.Define("/", new FunctionNode(Except));
            context.Define("<", new FunctionNode(Less));
            context.Define("do", new FunctionNode(_do));
            context.Define("if", new FunctionNode(IF));
            context.Define("=",new FunctionNode(Equal));
            context.Define("def",new FunctionNode(Def));
            context.Define("repeat",new FunctionNode(Repeat));
            context.Define("fn",new FunctionNode(Fn));
            context.Define("hash-map",new FunctionNode(HashMap));
            context.Define("map",new FunctionNode(_map));
            context.Define("apply",new FunctionNode(_apply));
            context.Define("case",new FunctionNode(_case));
        }

      
        public static List<ASTNode> EvalArgs(List<ASTNode> args, Context context)
        {
            List<ASTNode> newLst = new List<ASTNode>();
            for (int i = 0; i < args.Count; i++)
            {
                newLst.Add(RT.Evaluate(args[i], context));
            }
            return newLst;
        }



        static ASTNode _case(List<ASTNode> args, Context context)
        {
            ASTNode caseNode = RT.Evaluate(args[0], context);
            bool isCase = false;
            for (int i = 1; i < args.Count; i += 2)
            {
                if (args.Count <= i + 1)
                {
                    break;
                }
                ASTNode curKeyNode = RT.Evaluate(args[i], context);
                if (curKeyNode.NodeType == caseNode.NodeType)
                {
                    switch (caseNode.NodeType)
                    {
                        case ASTNode.ASTNodeType.NumberNode:
                            NumberNode curNumberNode = (NumberNode)curKeyNode;
                            switch (curNumberNode.Type)
                            {
                                case NumberNode.NumberType.INT:
                                    if ((int)curNumberNode.NumberVal == (int)((NumberNode)caseNode).NumberVal)
                                    {
                                        isCase = true;
                                        return RT.Evaluate(args[i + 1], context);
                                    }
                                    break;
                            }
                            break;
                        case ASTNode.ASTNodeType.StringNode:
                            StringNode curStringNode = (StringNode)curKeyNode;
                            if (curStringNode.Val == ((StringNode)caseNode).Val)
                            {
                                isCase = true;
                                return RT.Evaluate(args[i + 1], context);
                            }
                            break;
                    }
                }
            }
            if (isCase == false && args.Count % 2 == 0)
            {
                return RT.Evaluate(args[args.Count - 1], context);
            }
            return null;
        }

        public static ASTNode HashMap(List<ASTNode> args, Context context)
        {
            MapNode retMapNode = new MapNode();
            retMapNode.Values = new Hashtable();
            for (int i=0;i<args.Count;i+=2)
            {
                retMapNode.Values[args[i]] = args[i+1];
            }
            return retMapNode;
        }
        /*
          (fn [x y z] (str "fuck kkk"))
         */
        public static ASTNode Fn(List<ASTNode> args, Context context)
        {
            VectorNode argNode =  (VectorNode)args[0];
            FunctionNode FnNode = new FunctionNode();
            FnNode.FnType = FunctionNode.FuncType.SGLisp;
            ListNode doNode = new ListNode();
            doNode.nodes = new LinkedList<ASTNode>();
            doNode.nodes.AddLast(new SymbolNode("do"));
            for (int i=0;i<args.Count;i++)
            {
                doNode.nodes.AddLast(args[i]);
            }
            FnNode.lstNode = doNode;
            FnNode.vecArgNode = argNode;
            return FnNode;
        }
        public static ASTNode Repeat(List<ASTNode> args, Context context) 
        {
            return null;
        }

        public static ASTNode Equal(List<ASTNode> args, Context context)
        {
            BoolNode b = new BoolNode();
            b.Val = false;
            args = EvalArgs(args,context);
            if (args[0].NodeType == ASTNode.ASTNodeType.NumberNode && args[1].NodeType == ASTNode.ASTNodeType.NumberNode)
            {
                NumberNode number1 = (NumberNode)args[0];
                NumberNode number2 = (NumberNode)args[1];
                if (number1.Type == NumberNode.NumberType.INT && number2.Type == NumberNode.NumberType.INT)
                {
                    if ((int)number1.NumberVal == (int)number2.NumberVal)
                    {
                        b.Val = true;
                    }
                    else
                    {
                        b.Val = false;
                    }
                }
            }
            else if (args[0].NodeType == ASTNode.ASTNodeType.ObjectNode && args[1].NodeType == ASTNode.ASTNodeType.ObjectNode)
            {
                if (((ObjectNode)args[0]).Val == ((ObjectNode)args[1]).Val)
                {
                    b.Val = true;
                }
                else
                {
                    b.Val = false;
                }
            }
            else if (args[0].NodeType == ASTNode.ASTNodeType.BoolNode && args[1].NodeType == ASTNode.ASTNodeType.BoolNode)
            {
                if (((BoolNode)args[0]).Val == ((BoolNode)args[1]).Val)
                {
                    b.Val = true;
                }
            }
            else if (args[0].NodeType == ASTNode.ASTNodeType.StringNode && args[1].NodeType == ASTNode.ASTNodeType.StringNode)
            {
                if (((StringNode)args[0]).Val == ((StringNode)args[1]).Val)
                {
                    b.Val = true;
                }
            }
            return b;
        }

        public static ASTNode Def(List<ASTNode> args, Context context)
        {
            string varName = ((SymbolNode)args[0]).Val;
            context.Define(varName, RT.Evaluate(args[1], context));
            return null;
        }
    

        static ASTNode _sub(List<ASTNode> args, Context context)
        {
            NumberNode retNode = new NumberNode();
            retNode.NumberVal = ((NumberNode)RT.Evaluate(args[0], context)).NumberVal;
            args.RemoveAt(0);
            while (args.Count > 0)
            {
                NumberNode numNode = (NumberNode)RT.Evaluate(args[0], context);
                args.RemoveAt(0);
                switch (numNode.Type)
                {
                    case NumberNode.NumberType.INT:
                        if (retNode.Type == NumberNode.NumberType.INT)
                        {
                            retNode.NumberVal = (int)retNode.NumberVal - (int)numNode.NumberVal;
                        }
                        else
                        {
                            retNode.NumberVal = (float)retNode.NumberVal - (int)numNode.NumberVal;
                        }
                        break;
                    case NumberNode.NumberType.FLOAT:
                        retNode.Type = NumberNode.NumberType.FLOAT;
                        if (retNode.Type == NumberNode.NumberType.INT)
                        {
                            retNode.NumberVal = (float)retNode.NumberVal - (int)numNode.NumberVal;
                        }
                        else
                        {
                            retNode.NumberVal = (float)retNode.NumberVal - (float)numNode.NumberVal;
                        }
                        break;
                }
            }
            return retNode;
        }
        public static ASTNode Add(List<ASTNode> args, Context context) 
        {
            NumberNode retNode = new NumberNode();
            retNode.NumberVal = 0f;
            bool isAllInt = true;
            while(args.Count>0)
            {
              NumberNode numNode = (NumberNode)RT.Evaluate(args[0],context);
              args.RemoveAt(0);
              object curNumber = numNode.NumberVal;
              if (numNode.Type == NumberNode.NumberType.FLOAT)
              {
                    isAllInt = false;
              }
              
                if (numNode.NumberVal is int)
                {
                    curNumber = (float)(int)numNode.NumberVal;
                }
              retNode.NumberVal = (float)retNode.NumberVal + (float)curNumber;
            }
            if (isAllInt)
            {
                retNode.Type = NumberNode.NumberType.INT;
                retNode.NumberVal = (int)(float)retNode.NumberVal;
            }
            return retNode;
        }

        public static ASTNode IF(List<ASTNode> args, Context context)
        {
            args[0] =RT.Evaluate(args[0], context);
            if (((BoolNode)args[0]).Val)
            {
                RT.Evaluate(args[1], context);
            }
            else
            {
                if (args.Count == 3) 
                {
                    RT.Evaluate(args[2],context);
                }
            }
            return null;
        }

        //<
        public static ASTNode Less(List<ASTNode> args, Context context) 
        {
            BoolNode boolNode = new BoolNode();
            NumberNode numberNode0 = (NumberNode)RT.Evaluate(args[0], context);
            NumberNode numberNode1 = (NumberNode)RT.Evaluate(args[1], context);

            float num0 = numberNode0.Type == NumberNode.NumberType.INT ?(float)(int)numberNode0.NumberVal:(int)numberNode0.NumberVal;
            float num1 = numberNode1.Type == NumberNode.NumberType.INT ? (float)(int)numberNode1.NumberVal : (int)numberNode1.NumberVal;
              
            if (num0 < num1)
            {
                boolNode.Val = true;
            }
            else 
            {
                boolNode.Val = false;   
            }
            return boolNode;
        }

        public static ASTNode _do(List<ASTNode> args, Context context)
        {
            for (int i = 0; i < args.Count -1 ;i++)
            {
                RT.Evaluate(args[i], context);
            }
            return RT.Evaluate(args[args.Count - 1], context);
        }
         
        public static ASTNode Except(List<ASTNode> args,Context context) 
        {
            NumberNode numberNode0 = (NumberNode)RT.Evaluate(args[0], context);
            NumberNode numberNode1 = (NumberNode)RT.Evaluate(args[1], context);
            float num0 = numberNode0.Type == NumberNode.NumberType.INT ? (float)(int)numberNode0.NumberVal : (int)numberNode0.NumberVal;
            float num1 = numberNode1.Type == NumberNode.NumberType.INT ? (float)(int)numberNode1.NumberVal : (int)numberNode1.NumberVal;
            NumberNode retNumberNode =  new NumberNode();
            retNumberNode.NumberVal = num0/num1;
            retNumberNode.Type = NumberNode.NumberType.FLOAT;
            return retNumberNode; 
        }

        static ASTNode _apply(List<ASTNode> args,Context context)
        {

            return null;
        }

        static ASTNode _map(List<ASTNode> args, Context context)
        {
            args = CoreFunction.EvalArgs(args,context);
            FunctionNode FuncNode = ((FunctionNode)args[0]);
            VectorNode vecNode = (VectorNode)args[1];
            for (int i=0;i<vecNode.nodes.Length;i++)
            {
                FuncNode.Exec(new List<ASTNode>() {vecNode.nodes[i] },context);
            }
            return null;
        }

        static ASTNode _mul(List<ASTNode> args,Context context)
        {
            NumberNode retNode = new NumberNode();
            NumberNode firstNode = (NumberNode)RT.Evaluate(args[0], context);
            bool isAllInt = true;
            object curNumber = firstNode.NumberVal;
            if (firstNode.Type == NumberNode.NumberType.INT)
            {
                curNumber = (float)(int)firstNode.NumberVal;

            }
            else
            {
                isAllInt = false;
            }
            args.RemoveAt(0);
            while (args.Count > 0)
            {
                NumberNode numNode = (NumberNode)RT.Evaluate(args[0], context);
                args.RemoveAt(0);
                object curNumber2 = numNode.NumberVal;
                if (numNode.Type == NumberNode.NumberType.FLOAT)
                {
                    isAllInt = false;
                }

                if (numNode.Type == NumberNode.NumberType.INT)
                {
                    curNumber2 = (float)(int)numNode.NumberVal;
                }
                retNode.NumberVal = (float)curNumber2 * (float)curNumber;
            }
            if (isAllInt)
            {
                retNode.Type = NumberNode.NumberType.INT;
                retNode.NumberVal = (int)(float)retNode.NumberVal;
            }
            return retNode;
        }
    }
}
 